﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using DG.Common.EF.WPF;
using TCC.ControleFluxoDeCaixa.Model.Contas;
using TCC.ControleFluxoDeCaixa.Model.Gerenciadores;

namespace TCC.ControleFluxoDeCaixa.Model.Fluxo
{
    public class LancamentoRecorrente 
    {
        [Key]
        [Browsable(false)]
        public int LancamentoRecorrenteId { get; set; }
        [Required]
        public DateTime DataInicio { get; set; }
        public DateTime? DataFim { get; set; }

        [FilteredAtributte(false)]
        public virtual ICollection<LancamentoRecorrenteVencimento> Lancamentos { get; set; }
        [Required]
        [Display(Name = @"Valor Esperado Absoluto")]
        [FilteredAtributte(false)]
        public decimal ValorEsperadoAbsoluto { get; set; }
        [Required]
        public virtual Categoria Categoria { get; set; }
        [Required]
        public virtual ContaFinanceira Conta { get; set; }
        [Required]
        [Display(Name = @"Tipo de Movimento")]
        public TipoDeMovimento TipoDeMovimento { get; set; }
        [Required]
        public string Nome { get; set; }

        public void AtualizarListaDeVencimentos()
        {
            var pagos = new HashSet<DateTime>(Lancamentos.Select(e => e.Vencimento));
            var vencimentos = GetVencimentos().ToArray();
            var novosVencimentos = new HashSet<DateTime>(vencimentos.Except(pagos).ToArray());
            var vencimentosNull = Lancamentos.Where(l => l.Lancamento == null && !vencimentos.Contains(l.Vencimento)).ToArray();
            foreach (var vencimento in vencimentosNull)
            {
                Lancamentos.Remove(vencimento);
            }
            foreach (var vencimento in novosVencimentos)
            {
                Lancamentos.Add(new LancamentoRecorrenteVencimento
                {
                    LancamentoRecorrente = this, Vencimento = vencimento
                });
            }
        }

        public IEnumerable<DateTime> GetVencimentos()
        {
            var dataFim = DataFim.HasValue ? DataFim.Value.Date : DateTime.Now.Date.AddMonths(24);
            var tempData = DataInicio.Date;

            while (tempData <= dataFim)
            {
                yield return GerenciadorDeDatas.GetInstance().DiaUtil(tempData);
                foreach (var unidadeTempo in UnidadesDeRecorrencia.OrderByDescending(u=> u.UnidadeTempo))
                {
                    switch (unidadeTempo.UnidadeTempo)
                    {
                        case UnidadeTempo.Dia:
                            tempData = tempData.AddDays(unidadeTempo.Valor);
                            break;
                        case UnidadeTempo.Mes:
                            tempData = tempData.AddMonths(unidadeTempo.Valor);
                            break;
                        case UnidadeTempo.Ano:
                            tempData = tempData.AddYears(unidadeTempo.Valor);
                            break;
                        case UnidadeTempo.Semana:
                            tempData = tempData.AddDays(7*unidadeTempo.Valor);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
        }

        public IEnumerable<DateTime> GetVencimentosNaoPagos()
        {
            return Lancamentos.Where(item => item.Lancamento == null).Select(i => i.Vencimento).ToArray();

        }

        [FilteredAtributte(false)]
        public DateTime? ProximoPagamentoEm
        {
            get
            {
                var naoPagos = GetVencimentosNaoPagos().ToArray();
                return naoPagos.Any() ? (DateTime?) naoPagos.Min() : null;
            }
        }
        

        public LancamentoRecorrente()
        {
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            this.Lancamentos = new HashSet<LancamentoRecorrenteVencimento>();
            this.UnidadesDeRecorrencia = new HashSet<LancamentoRecorrenteUnidadeTempo>();
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        public decimal ValorEsperado
        {
            get { return ValorEsperadoAbsoluto*(TipoDeMovimento == TipoDeMovimento.Despesa ? -1 : 1); }
        }

        [FilteredAtributte(false)]
        public virtual ICollection<LancamentoRecorrenteUnidadeTempo> UnidadesDeRecorrencia { get; set; }
        
        public override string ToString()
        {
            return string.Format("Inicio: {0}, Fim: {1}, Categoria: {2}", DataInicio, DataFim, Categoria);
        }
    }
}
